package com.gitee.kamismile.stone.basic.data.application.service;

import com.gitee.kamismile.stone.basic.data.application.req.command.user.*;
import com.gitee.kamismile.stone.basic.data.application.req.query.role.RoleQuery;
import com.gitee.kamismile.stone.basic.data.application.req.query.role.RoleResQuery;
import com.gitee.kamismile.stone.basic.data.application.req.query.user.*;
import com.gitee.kamismile.stone.basic.data.domain.sysRes.repository.entity.SysResources;
import com.gitee.kamismile.stone.basic.data.domain.sysRes.service.ISysResDomainService;
import com.gitee.kamismile.stone.basic.data.domain.sysRole.aggregate.enums.GradeEnum;
import com.gitee.kamismile.stone.basic.data.domain.sysRole.repository.entity.SysRole;
import com.gitee.kamismile.stone.basic.data.domain.sysRole.repository.entity.SysRoleResources;
import com.gitee.kamismile.stone.basic.data.domain.sysRole.service.ISysRoleDomainService;
import com.gitee.kamismile.stone.basic.data.domain.sysUser.repository.entity.*;
import com.gitee.kamismile.stone.basic.data.domain.sysUser.service.ISysUserDomainService;
import com.gitee.kamismile.stone.basic.data.infrastructure.enums.CommonErrorEnum;
import com.gitee.kamismile.stone.basic.data.infrastructure.enums.RoleErrorEnum;
import com.gitee.kamismile.stone.basic.data.infrastructure.enums.UserErrorEnum;
import com.gitee.kamismile.stone.basic.data.infrastructure.utils.CriteriaQueryBuilder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Component
public class SysUserServiceApplicationService {
    @Autowired
    private ISysUserDomainService iSysUserDomainService;
    @Autowired
    private ISysRoleDomainService iSysRoleDomainService;
    @Autowired
    private ISysResDomainService iSysResDomainService;
    public Mono<SysDepUser> save(UserDepCreate userDepCreate, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        SysDepUser sysDepUser = new SysDepUser();
        BeanUtils.copyProperties(userDepCreate, sysDepUser);
        sysDepUser.setnTenantId(nTenantId);
        return iSysUserDomainService.saveOrUpdate(sysDepUser);
    }


    public Mono<SysDepUser> update(UserDepEdit userDepEdit, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(userDepEdit.getnSysDepUserId(), UserErrorEnum.UserDepNotNull.getMessage());
        ;

        return iSysUserDomainService.findAndCheckUserDepById(userDepEdit.getnSysDepUserId(), nTenantId)
                .flatMap(v -> {
                    SysDepUser sysDepUser = new SysDepUser();
                    BeanUtils.copyProperties(userDepEdit, sysDepUser);
                    return iSysUserDomainService.saveOrUpdate(sysDepUser);
                });
    }


    public Mono<Void> deleteUserDepById(Integer nSysDepUserId, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(nSysDepUserId, UserErrorEnum.UserDepNotNull.getMessage());

        return iSysUserDomainService.findAndCheckUserDepById(nSysDepUserId, nTenantId)
                .flatMap(v -> iSysUserDomainService.deleteUserDepById(nSysDepUserId)
                );
    }


    public Mono<Void> deleteUserDepByUserId(Integer nUserId, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(nUserId, UserErrorEnum.UserNotNull.getMessage());
        return iSysUserDomainService.deleteUserDepByUserId(nUserId, nTenantId);
    }

    public Mono<Void> deleteUserRoleByUserId(Integer nUserId, Integer nTenantId){
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(nUserId, UserErrorEnum.UserNotNull.getMessage());
        return iSysUserDomainService.deleteUserRoleByUserId(nUserId, nTenantId);
    }

    public Mono<Page<SysDepUser>> findUserDepPage(UserDepQuery userDepQuery) {
        Assert.notNull(userDepQuery.getnTenantId(), CommonErrorEnum.TenantNotFound.getMessage());
        Criteria criteria = CriteriaQueryBuilder.builder()
                .inIgnoreEmpty("nTenantId", new Object[]{0, userDepQuery.getnTenantId()})
                .eqIgnoreEmpty("nSysDepId",userDepQuery.getnSysDepId())
                .create();
        return iSysUserDomainService.findUserDepPage(userDepQuery.getCurrentPage(), userDepQuery.getPageSize(), criteria);
    }


    public Flux<SysRoleUser> saveBatch(UserRoleCreate userRoleCreate, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());

        if(CollectionUtils.isEmpty(userRoleCreate.getnSysRoleIds())){
            return Flux.empty();
        }

        List<SysRoleUser> sysRoleUsers = userRoleCreate.getnSysRoleIds().stream().map(v -> {
            SysRoleUser sysRoleUser = new SysRoleUser();
            BeanUtils.copyProperties(userRoleCreate, sysRoleUser);
            sysRoleUser.setnSysRoleId(v);
            sysRoleUser.setnTenantId(nTenantId);
            return sysRoleUser;
        }).collect(Collectors.toList());
        return iSysUserDomainService.saveBatch(sysRoleUsers);
    }


    public Mono<SysRoleUser> update(UserRoleEdit userRoleEdit, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(userRoleEdit.getnSysRoleUserId(), UserErrorEnum.UserRoleNotNull.getMessage());
        ;

        return iSysUserDomainService.findAndCheckUserRoleById(userRoleEdit.getnSysRoleUserId(), nTenantId)
                .flatMap(v -> {
                    SysRoleUser sysRoleUser = new SysRoleUser();
                    BeanUtils.copyProperties(userRoleEdit, sysRoleUser);
                    return iSysUserDomainService.saveOrUpdate(sysRoleUser);
                });
    }


    public Mono<Void> deleteUserRoleById(Integer nSysRoleUserId, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(nSysRoleUserId, UserErrorEnum.UserRoleNotNull.getMessage());

        return iSysUserDomainService.findAndCheckUserRoleById(nSysRoleUserId, nTenantId)
                .flatMap(v -> iSysUserDomainService.deleteUserRoleById(nSysRoleUserId)
                );
    }


    public Mono<Page<SysRoleUser>> findUserRolePage(UserRoleQuery userDepQuery) {
        Assert.notNull(userDepQuery.getnTenantId(), CommonErrorEnum.TenantNotFound.getMessage());
        Criteria criteria = CriteriaQueryBuilder.builder()
                .inIgnoreEmpty("nTenantId", new Object[]{0, userDepQuery.getnTenantId()})
                .eqIgnoreEmpty("nUserId", userDepQuery.getnUserId())
                .create();
        return iSysUserDomainService.findUserRolePage(userDepQuery.getCurrentPage(), userDepQuery.getPageSize(), criteria);
    }


    public Mono<SysUserResources> save(UserResourcesCreate userResourcesCreate, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        SysUserResources sysUserResources = new SysUserResources();
        BeanUtils.copyProperties(userResourcesCreate, sysUserResources);
        sysUserResources.setnTenantId(nTenantId);
        return iSysUserDomainService.saveOrUpdate(sysUserResources);
    }


    public Mono<SysUserResources> update(UserResourcesEdit userResourcesEdit, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(userResourcesEdit.getnSysUserResourcesId(), UserErrorEnum.UserResNotNull.getMessage());
        ;

        return iSysUserDomainService.findAndCheckUserResById(userResourcesEdit.getnSysUserResourcesId(), nTenantId)
                .flatMap(v -> {
                    SysUserResources sysUserResources = new SysUserResources();
                    BeanUtils.copyProperties(userResourcesEdit, sysUserResources);
                    return iSysUserDomainService.saveOrUpdate(sysUserResources);
                });
    }

    public Mono<Void> deleteUserResById(Integer nSysUserResourcesId, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(nSysUserResourcesId, UserErrorEnum.UserResNotNull.getMessage());
        ;

        return iSysUserDomainService.findAndCheckUserResById(nSysUserResourcesId, nTenantId)
                .flatMap(v -> iSysUserDomainService.deleteUserResById(nSysUserResourcesId)
                );
    }


    public Mono<Page<SysUserResources>> findUserResPage(UserResQuery query) {
        Assert.notNull(query.getnTenantId(), CommonErrorEnum.TenantNotFound.getMessage());
        Criteria criteria = CriteriaQueryBuilder.builder()
                .inIgnoreEmpty("nTenantId", new Object[]{0, query.getnTenantId()})
                .eqIgnoreEmpty("nUserId", query.getnUserId())
                .create();
        return iSysUserDomainService.findUserResPage(query.getCurrentPage(), query.getPageSize(), criteria);
    }


    /**
     * 根据用户ID
     *
     * @param query
     * @return
     */
    public Flux<SysResources> findAllUserRes(UserResQuery query) {
        Assert.notNull(query.getnTenantId(), CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(query.getnUserId(), UserErrorEnum.UserNotNull.getMessage());
        return Mono.zip(findAllUserResIds(query),findAllUserRoleResIds(query))
                .flatMapMany(tuple -> {
                    List<Integer> combinedList = new ArrayList<>();
                    combinedList.addAll(tuple.getT1());
                    combinedList.addAll(tuple.getT2());

                    if(CollectionUtils.isEmpty(combinedList)){
                        return Flux.empty();
                    }

                    return this.iSysResDomainService.findResByIds(combinedList,query.getsType(),query.getsParentType(), query.getnParentId());
                });

    }


    public Mono<SysUserSetup> save(UserSetupCreate userResourcesCreate, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        SysUserSetup sysUserSetup = new SysUserSetup();
        BeanUtils.copyProperties(userResourcesCreate, sysUserSetup);
        sysUserSetup.setnTenantId(nTenantId);
        return iSysUserDomainService.saveOrUpdate(sysUserSetup);
    }


    public Mono<SysUserSetup> update(UserSetupEdit userSetupEdit, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(userSetupEdit.getnUserSetupId(), UserErrorEnum.UserSetupNotNull.getMessage());
        ;

        return iSysUserDomainService.findAndCheckUserSetupById(userSetupEdit.getnUserSetupId(), nTenantId)
                .flatMap(v -> {
                    SysUserSetup sysUserSetup = new SysUserSetup();
                    BeanUtils.copyProperties(userSetupEdit, sysUserSetup);
                    return iSysUserDomainService.saveOrUpdate(sysUserSetup);
                });
    }

    public Mono<Void> deleteUserSetupById(Integer nUserSetupId, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(nUserSetupId, UserErrorEnum.UserSetupNotNull.getMessage());
        ;

        return iSysUserDomainService.findAndCheckUserSetupById(nUserSetupId, nTenantId)
                .flatMap(v -> iSysUserDomainService.deleteUserSetupById(nUserSetupId)
                );
    }


    public Mono<Page<SysUserSetup>> findUserSetupPage(UserSetupQuery query) {
        Assert.notNull(query.getnTenantId(), CommonErrorEnum.TenantNotFound.getMessage());
        Criteria criteria = CriteriaQueryBuilder.builder()
                .inIgnoreEmpty("nTenantId", new Object[]{0, query.getnTenantId()})
                .create();
        return iSysUserDomainService.findUserSetupPage(query.getCurrentPage(), query.getPageSize(), criteria);
    }


    public Mono<SysUserShortcut> save(UserShortcutCreate userShortcutCreate, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(userShortcutCreate.getnUserId(), UserErrorEnum.UserNotNull.getMessage());

        SysUserShortcut sysUserShortcut = new SysUserShortcut();
        BeanUtils.copyProperties(userShortcutCreate, sysUserShortcut);
        sysUserShortcut.setnTenantId(nTenantId);
        return iSysUserDomainService.saveOrUpdate(sysUserShortcut);
    }


    public Mono<SysUserShortcut> update(UserShortcutEdit userShortcutEdit, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(userShortcutEdit.getnMenuId(), UserErrorEnum.UserShortcutNotNull.getMessage());

        return iSysUserDomainService.findAndCheckUserShortcutByResId(userShortcutEdit.getnMenuId(),userShortcutEdit.getnUserId(), nTenantId)
                .flatMap(v -> {
                    SysUserShortcut sysUserShortcut = new SysUserShortcut();
                    BeanUtils.copyProperties(userShortcutEdit, sysUserShortcut);
                    sysUserShortcut.setnSysUserShortcutId(v.getnSysUserShortcutId());
                    return iSysUserDomainService.saveOrUpdate(sysUserShortcut);
                });
    }

    public Mono<Void> deleteUserShortByResId(Integer nMenuId,Integer nUserId, Integer nTenantId) {
        Assert.notNull(nTenantId, CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(nMenuId, UserErrorEnum.UserShortcutNotNull.getMessage());

        return iSysUserDomainService.findAndCheckUserShortcutByResId(nMenuId, nUserId, nTenantId)
                .flatMap(v -> iSysUserDomainService.deleteUserShortById(v.getnSysUserShortcutId())
                );
    }


    public Mono<Page<SysUserShortcut>> findUserShortPage(UserShortcutQuery query) {
        Assert.notNull(query.getnTenantId(), CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(query.getnUserId(), UserErrorEnum.UserNotNull.getMessage());
        Criteria criteria = CriteriaQueryBuilder.builder()
                .inIgnoreEmpty("nTenantId", new Object[]{0, query.getnTenantId()})
                .eqIgnoreEmpty("nUserId", query.getnUserId())
                .create();
        return iSysUserDomainService.findUserShortPage(query.getCurrentPage(), query.getPageSize(), criteria);
    }


    public Mono<Page<SysRoleResources>> findRoleSimpleResPage(RoleResQuery roleQuery) {
        Assert.notNull(roleQuery.getnTenantId(), CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(roleQuery.getnSysRoleIds(), RoleErrorEnum.RoleNotNull.getMessage());
        Criteria criteria = CriteriaQueryBuilder.builder()
                .inIgnoreEmpty("nSysRoleId", roleQuery.getnSysRoleIds())
                .inIgnoreEmpty("nTenantId", new Object[]{0, roleQuery.getnTenantId()})
                .create();
        return iSysRoleDomainService.findRoleResPage(roleQuery.getCurrentPage(), roleQuery.getPageSize(), criteria);
    }

    public Mono<List<Integer>> findAllUserRoleResIds(UserResQuery query) {
        Assert.notNull(query.getnTenantId(), CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(query.getnUserId(), UserErrorEnum.UserNotNull.getMessage());
        UserRoleQuery userRoleQuery = new UserRoleQuery();
        userRoleQuery.setnUserId(query.getnUserId());
        userRoleQuery.setnTenantId(query.getnTenantId());
        userRoleQuery.setCurrentPage(0);
        userRoleQuery.setPageSize(3000);
        return findUserRolePage(userRoleQuery)
                .flatMap(roleUserPage->{
                    List<SysRoleUser> userRoles = roleUserPage.getContent();
                    if (CollectionUtils.isEmpty(userRoles)) {
                        return Mono.just(new ArrayList<>());
                    }
                    List<Integer> roleIds = userRoles.stream().map(SysRoleUser::getnSysRoleId).filter(Objects::nonNull).toList();
                    if (CollectionUtils.isEmpty(roleIds)) {
                        return Mono.just(new ArrayList<>());
                    }


                    RoleQuery roleQuery = new RoleQuery();
                    roleQuery.setnSysRoleIds(roleIds);
                    roleQuery.setnTenantId(query.getnTenantId());
                    roleQuery.setCurrentPage(0);
                    roleQuery.setPageSize(3000);
                   return findAdminRolePage(roleQuery)
                           .flatMap(v->{
                               List<SysRole> adminRole = v.getContent();
                               if(!CollectionUtils.isEmpty(adminRole)){
                                  return iSysResDomainService.findResByIds(null, query.getsType(), query.getsParentType(), query.getnParentId())
                                          .map(SysResources::getnResourcesId)
                                          .collectList();
                               }


                               RoleResQuery roleResQuery = new RoleResQuery();
                               roleResQuery.setnSysRoleIds(roleIds);
                               roleResQuery.setnTenantId(query.getnTenantId());
                               roleResQuery.setCurrentPage(0);
                               roleResQuery.setPageSize(3000);

                               return findRoleSimpleResPage(roleResQuery)
                                       .flatMap(roleResPages->{
                                           List<SysRoleResources> content = roleResPages.getContent();
                                           if (CollectionUtils.isEmpty(content)) {
                                               return Mono.just(new ArrayList<>());
                                           }
                                           return Mono.just( content.stream().map(SysRoleResources::getnResourcesId).toList());
                                       });
                           });

                });

    }

    public Mono<Page<SysRole>> findAdminRolePage(RoleQuery roleQuery){
        Assert.notNull(roleQuery.getnTenantId(), CommonErrorEnum.TenantNotFound.getMessage());
        Criteria criteria = CriteriaQueryBuilder.builder()
                .inIgnoreEmpty("nSysRoleId",roleQuery.getnSysRoleIds())
                .inIgnoreEmpty("nTenantId", new Object[]{0,roleQuery.getnTenantId()})
                .eqIgnoreEmpty("sGrade", GradeEnum.SupperAdmin.getCode())
                .create();
        return iSysRoleDomainService.findRolePage(roleQuery.getCurrentPage(),roleQuery.getPageSize(),criteria);
    }

    public Mono<List<Integer>> findAllUserResIds(UserResQuery query) {
        Assert.notNull(query.getnTenantId(), CommonErrorEnum.TenantNotFound.getMessage());
        Assert.notNull(query.getnUserId(), UserErrorEnum.UserNotNull.getMessage());
        query.setCurrentPage(0);
        query.setPageSize(3000);
        Criteria criteria = CriteriaQueryBuilder.builder()
                .inIgnoreEmpty("nTenantId", new Object[]{0, query.getnTenantId()})
                .eqIgnoreEmpty("nUserId", query.getnUserId())
                .create();
        return iSysUserDomainService.findUserResPage(query.getCurrentPage(), query.getPageSize(), criteria)
                .flatMap(userResPages->{
                    List<SysUserResources> content = userResPages.getContent();
                    if (CollectionUtils.isEmpty(content)) {
                        return Mono.just(new ArrayList<>());
                    }
                    List<Integer> resIds = content.stream().map(SysUserResources::getnResourcesId).filter(Objects::nonNull).toList();
                    if (CollectionUtils.isEmpty(resIds)) {
                        return Mono.just(new ArrayList<>());
                    }
                    return Mono.just(resIds);
                }) ;
    }
}
